
----------------------------------------------------------------
--- Copyright：  迷你玩-海马
--- Description：data_cache(dc) 模块的公共代码库
---  1 公共函数
---  2 公共模块：统计
----------------------------------------------------------------


local assert     = assert
local require    = require
local bit        = bit
local coroutine  = coroutine
local debug      = debug
local io         = io
local pairs      = pairs
local ipairs     = ipairs
local math       = math
local os         = os
local print      = print
local pcall      = pcall
local xpcall     = xpcall
local rawget     = rawget
local rawset     = rawset
local select     = select
local string     = string
local table      = table
local tonumber   = tonumber
local tostring	 = tostring
local error      = error
local type       = type
local unpack     = unpack
local setmetatable = setmetatable
local getmetatable = getmetatable
local ngx = ngx

local string_char  = string.char
local string_sub   = string.sub
local string_byte  = string.byte
local string_gsub  = string.gsub
local string_find  = string.find
local string_upper = string.upper

local math_fmod   = math.fmod
local math_floor  = math.floor
local math_pow    = math.pow
local math_abs    = math.abs

local table_tostring = table.tostring
local ngx_shared     = ngx.shared

----数据变量----
local uu           = require( "modules.commhm.utils" )
local log          = require( "modules.commhm.log" )

local ns_env       = require( "modules.env.env" );

local CNgxTimerWrap = require( "modules.commhm.ngxTimerWrap" )

local ns_data_cache_ip_allow = require( "modules.data_cache.data_cache_ip_allow" )
local ns_centerServerProxy   = require("modules.commhm.centerServerProxy")



local FOO = {
	_VERSION = '0.12.1',

	-- double_check_tj    -- 二次校验统计
	-- byte_tj            -- 流量统计

	report_ServerMonitor_name =  "dc" .. (ns_env.report_ServerMonitor_name or '');

	ver_lua = 'dc_1124_02f',
	ver_hot = 0,

	worker_id = ngx.worker.id(),
};



---检查ip白名单
function FOO.check_ip_allow_by_name()
	return ns_data_cache_ip_allow.check_ip_allow( ngx.ctx.m_params.name, ngx.var.remote_addr )
end


---延迟执行一个任务
FOO.timer_at = function( delay_, func_, params_ )
	local timer_ = CNgxTimerWrap:new()
	return timer_:ngx_timer_at( delay_, func_,  params_ )
end


---定期执行一个任务
FOO.timer_every = function( delay_, func_, params_ )
	local timer_ = CNgxTimerWrap:new()
	return timer_:ngx_timer_every( delay_, func_, params_  )
end



-- key hash 二次确认---------------------------------------------
FOO.double_check_tj = {}    -- { cc=0, nf=0, kh=0, kh2=0, no_kh=0 }

-- 检查数据key hash的正确性
-- 如果数据中，包含下面的结构体 { _k_=12345678, _h_=2, ... ... } 可以进行一次数据校验
function FOO.check_ret_string_kh( ret_, key_, hash_, name_, log_name_ )

	if  not FOO.double_check_tj[ name_ ] then
		FOO.double_check_tj[ name_ ] = { cc=0, nf=0, kh=0, kh2=0, no_kh=0 }
	end

	local tj_name_ = FOO.double_check_tj[ name_ ]
	tj_name_.cc = tj_name_.cc + 1

	---是否二次检查结果的正确性
	if  ret_ == "not_found" then
		tj_name_.nf = tj_name_.nf + 1
	else
		if  string.find( ret_, '='..key_ ) then               --["_k_"]=1000001002
			---正常数据
			tj_name_.kh   = tj_name_.kh + 1
		elseif  string.find( ret_, '="'..key_ .. '"' ) then   --["_k_"]="402315353_1"
			---正常数据(字符串)
			tj_name_.kh2  = tj_name_.kh2 + 1
		else
			if  string.find( ret_, "_k_" ) then
				log.day_list_simple( log_name_, "error_recheck_kh", ngx.worker.id(), name_, key_, hash_, ret_ )
				return 1  --失败
			else
				---没有 _k_ 是旧数据
				tj_name_.no_kh  = tj_name_.no_kh + 1
			end
		end
	end

	return 0   --成功
end


-- 打印统计日志 30s
function FOO.print_double_check_tj( log_name_ )
	for k, v in pairs( FOO.double_check_tj ) do
		if  type(v) == 'table' then
			log.day_list_simple( log_name_, "recheck_tj", FOO.worker_id, k,
					'cc='..v.cc, 'nf='..v.nf, 'kh='..v.kh, 'kh2='..v.kh2, 'no_kh='..v.no_kh )
		end
	end
	FOO.double_check_tj = {}
end



-- 流量统计 -------------------------------------------------
FOO.byte_tj   = {}
FOO.select_tj = {}

-- 增加统计量
function FOO.add_byte_tj( name_, cmd_, byte_, select_, mread_count_ )
	if  not FOO.byte_tj[ name_ ] then
		FOO.byte_tj[ name_ ] = {}
	end

	if  not FOO.byte_tj[ name_ ][ cmd_ ] then
		FOO.byte_tj[ name_ ][ cmd_ ] = { c=0, b=0, select={} }
	end

	FOO.byte_tj[ name_ ][ cmd_ ].c = FOO.byte_tj[ name_ ][ cmd_ ].c + (mread_count_ or 1)
	FOO.byte_tj[ name_ ][ cmd_ ].b = FOO.byte_tj[ name_ ][ cmd_ ].b + byte_

	if  type(select_) == 'number' then
		local select_num_ = select_ % 10
		FOO.byte_tj[name_][cmd_].select[select_num_] = (FOO.byte_tj[name_][cmd_].select[select_num_] or 0) + (mread_count_ or 1)
	end

end


-- 打印统计日志 30s
function FOO.print_byte_tj( log_name_, dc_type_ )

	-- 所有worker保存数据
	uu.set_server_cache_table( FOO.worker_id, 'dc_tj_' .. FOO.worker_id, FOO.byte_tj, 600 )
	FOO.byte_tj = {}   --清洗上报数据

	-- 其中一个worker上报
	local code_ = uu.get_server_cache_string( 1, "print_byte_tj" )
	if  code_ then
		-- 其他已经上报
		log.day_list_simple( log_name_, "byte_tj_workers0", "ignored" , FOO.worker_id )
		return
	else
		uu.set_server_cache_string( 1, "print_byte_tj", FOO.worker_id, 30 )
		log.day_list_simple( log_name_, "byte_tj_workers0", "begin_report=====" , FOO.worker_id )
	end

	if  FOO.worker_id >= 0 then
		local report_monitor = { c=0, b=0 }   --上报给monitor的数据

		local tj_workers_ = {}    ---所有worker的统计
		local tj_selects_ = {}    ---所有select调用次数统计

		for i=0, ngx.worker.count() do
			local key_ = "dc_tj_" .. i
			local tj_  = uu.get_server_cache_table( i, key_ )
			if  tj_ then
				-- map  read2  c=4|b=415
				for name_, cmd_info_ in pairs( tj_ ) do
					if  not tj_workers_[name_] then
						tj_workers_[name_] = {}
					end
					for cmd_, info_ in pairs( cmd_info_ ) do

						if  not tj_workers_[name_][cmd_] then
							tj_workers_[name_][cmd_] = { c=0, b=0 }
						end
						tj_workers_[name_][cmd_].c = tj_workers_[name_][cmd_].c + info_.c
						tj_workers_[name_][cmd_].b = tj_workers_[name_][cmd_].b + info_.b

						if  info_.select then
							for k, v in pairs( info_.select ) do
								if  not tj_selects_[name_] then tj_selects_[name_] = { cc=0 } end
								tj_selects_[name_][k] = (tj_selects_[name_][k] or 0) + v
								tj_selects_[name_].cc = tj_selects_[name_].cc + v
							end
						end

						log.day_list_simple( log_name_, "byte_tj", i, name_, cmd_, 'c='..info_.c, 'b='..info_.b )

						report_monitor.c = report_monitor.c + info_.c
						report_monitor.b = report_monitor.b + info_.b
					end
				end
			end

		end


		local server_name_ = FOO.report_ServerMonitor_name

		--打印所有统计
		-- map  read2  c=4|b=415
		for name_, cmd_info_ in pairs( tj_workers_ ) do
			for cmd_, info_ in pairs( cmd_info_ ) do
				--14:43:11|-|0|byte_tj_workers1|0|rent|read31|c=105|b=8705
				log.day_list_simple( log_name_, "byte_tj_workers1", FOO.worker_id, name_, cmd_, 'c='..info_.c, 'b='..info_.b )

				local info_monitor = {
					from = FOO.report_ServerMonitor_name,
					name = name_,    --ma map rent
					cmd  = cmd_,     --read2  read3
					c = info_.c,     --call count
					b = info_.b,     --bytes
				}
				report_monitor[ #report_monitor + 1] = info_monitor
			end
			server_name_ = server_name_ .. '_' .. name_
		end

		log.day_list_simple( log_name_, "byte_tj_workers2", FOO.worker_id, report_monitor.c, report_monitor.b )

		local json_str_
		if  #report_monitor > 0 then
			report_monitor.select = tj_selects_
			--log.day_list_simple( log_name_, "byte_tj_workers3", FOO.worker_id, uu.to_str(tj_selects_) )
			json_str_ = uu.table2json( report_monitor )
			--log.day_list_simple( log_name_, "byte_tj_workers4", FOO.worker_id, json_str_ )
		end

		--上报到center
		ns_centerServerProxy.WWW_report_load( server_name_, "data_cache", dc_type_,
				report_monitor.c, report_monitor.b, nil, FOO.ver_lua, FOO.ver_hot, json_str_ )
	end

end



--从str中分离出第一个数值，作为select值:  1231411=1231411   12323_2k_342=12323
function FOO.get_select_by_string( str_ )
	local num_ = tonumber( str_ )
	if  not num_ then
		-- 尝试按下划线分隔，获得第一个key
		local t_ = uu.split( str_, '_' )
		if  t_ and t_[1] then
			num_ = tonumber( t_[1] )
		end
		log.debug( "split get_select_by_string=" .. (num_ or 0) )
	end
	return (num_ or 0)
end


return FOO;
